﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace Resource_Objects
{
    public class ExclusionManager
    {
        /// <summary>
        /// Gets or sets a list of regular expressions. Each match of these regular expressions that should be
        /// excluded from translation.
        /// </summary>
        public List<Exclusion> ExclusionPatterns { get; set; }

        private readonly IDictionary<string, string> _placeHolders = new Dictionary<string, string>();

        private const string PlaceHolderPattern = "[### {0} ###]";

        private IEnumerable<string> GetActiveExclusionPatterns()
        {
            if (this.ExclusionPatterns == null || this.ExclusionPatterns.Count == 0)
            {
                return new string[] { };
            }
            return this.ExclusionPatterns.Where(e => e.Enabled).Select(e => e.Pattern);
        }

        /// <summary>
        /// Replaces all matches of regular expressions found in <see cref="ExclusionPatterns"/> with placeholders
        /// that are not translatable by the translator.
        /// </summary>
        public string Exclude(string text)
        {
            if (this.ExclusionPatterns != null && this.ExclusionPatterns.Count > 0)
            {
                var captures = GetCaptures(text, this.GetActiveExclusionPatterns());
                foreach (var capture in captures)
                {
                    if (this.ExclusionPatterns != null && this.ExclusionPatterns.Count > 0)
                    {
                        text = text.Replace(capture.Value, this.GetPlaceHolder(capture.Value));
                    }
                }
            }

            return text;
        }

        /// <summary>
        /// Returns a list of captures of exclusions in the given text
        /// </summary>
        private static IEnumerable<Capture> GetCaptures(string text, IEnumerable<string> exclusionPatterns)
        {
            return from exclusionPattern in exclusionPatterns
                   select Regex.Matches(text, exclusionPattern)
                       into matches
                       from Match match in matches
                       from Group grp in match.Groups
                       from Capture capture in grp.Captures
                       select capture;
        }

        /// <summary>
        /// Gets a non translatable placeholder for the given text.
        /// </summary>
        private string GetPlaceHolder(string text)
        {
            if (!this._placeHolders.ContainsKey(text))
            {
                this._placeHolders.Add(text, string.Format(PlaceHolderPattern, this._placeHolders.Count));
            }
            return this._placeHolders[text];
        }

        /// <summary>
        /// Restores all placeholders created by <see cref="Exclude"/> with the original strings
        /// </summary>
        public string Restore(string text)
        {
            if (this._placeHolders != null && this._placeHolders.Count > 0)
            {
                string copyOfModifiedClosure = text;
                var placeHolders = this._placeHolders.Where(placeholder => copyOfModifiedClosure.IndexOf(placeholder.Value, StringComparison.InvariantCultureIgnoreCase) != -1);
                foreach (var placeholder in placeHolders)
                {
                    text = text.Replace(placeholder.Value, placeholder.Key);
                }
            }
            return text;
        }

        /// <summary>
        /// Validates if the given translation includes all the items of the source item that should be excluded
        /// from translation. Returns a list of item that was supposed to be exluded by they didn't.
        /// Returns null if translation is ok.
        /// </summary>
        public string[] Validate(string translation, string source)
        {
            //Get captures of excluded items in source
            var captures = GetCaptures(source, this.GetActiveExclusionPatterns());

            //Get the captures that do not exist in translation
            var notFoundCaptures = captures.Where(capture => translation.IndexOf(capture.Value, StringComparison.InvariantCulture) == -1).Select(capture => capture.Value);
            return notFoundCaptures.ToArray();
        }
    }
}
